package com.xj.lucene;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.document.*;
import org.apache.lucene.document.DateTools.Resolution;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.highlight.Formatter;
import org.apache.lucene.search.highlight.Highlighter;
import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.SimpleHTMLFormatter;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.*;
import java.util.List;

public class Demo {
	private static Directory dir = null;
	private static Analyzer analyzer = null;
	private IndexWriter indexWriter = null;
	private IndexReader dirReader = null;
	private static String[] names = { "那些年", "那些年我们", "那些年我们一起追过的女孩", "追过的女孩" };

	public Demo(boolean create) throws IOException {
		init(create);
	}

	static {
		try {
			dir = FSDirectory.open(new File("D://lucene/index"));
			analyzer = new IKAnalyzer();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public void init(boolean create) throws IOException {
		IndexWriterConfig iwc = new IndexWriterConfig(Version.LUCENE_42, analyzer);
		if (create) {
			iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
		} else {
			iwc.setOpenMode(IndexWriterConfig.OpenMode.APPEND);
		}
		LogMergePolicy mergePolicy = new LogDocMergePolicy();// new
																// LogByteSizeMergePolicy();
		// 达到3个文件时就和合并 MergePolicy
		mergePolicy.setMergeFactor(20);// SetMergeFactor是控制segment合并频率的，其决定了一个索引块中包括多少个文档，当硬盘上的索引块达到多少时，将它们合并成一个较大的索引块。当MergeFactor值较大时，生成索引的速度较快。MergeFactor的默认值是10，建议在建立索引前将其设置的大一些。
		mergePolicy.setUseCompoundFile(true);// 这个方法可以使Lucene在创建索引库时，会合并多个
												// Segments 文件到一个 .cfs
												// 中。此方式有助于减少索引文件数量，对于将来搜索的效率有较大影响。
		iwc.setMergePolicy(mergePolicy);
		indexWriter = new IndexWriter(dir, iwc);
	}

	/**
	 * 创建索引
	 * 
	 * @param create
	 * @throws IOException
	 * @throws Exception
	 */
	public void createIndex(String path) throws IOException {
		// Directory dir = new RAMDirectory();
		// iwc.setRAMBufferSizeMB(ramBufferSizeMB)
		/*
		 * Document doc = new Document(); doc.add(new Field("title", "标题",
		 * TextField.TYPE_STORED)); doc.add(new Field("content", "搜索的内容",
		 * TextField.TYPE_STORED)); indexWriter.addDocument(doc);
		 */
		try {
			this.readFile(path, indexWriter);
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			indexWriter.close();
		}

		/*
		 * IndexWriterConfig fsIndexWriterConfig = new
		 * IndexWriterConfig(Version.LUCENE_41,analyzer); //创建新的索引目录或者覆盖原来的索引目录
		 * fsIndexWriterConfig.setOpenMode(OpenMode.CREATE); IndexWriter
		 * fsIndexWriter = new IndexWriter(dir,fsIndexWriterConfig);
		 * //把内存中的索引库写到文件系统中 fsIndexWriter.addIndexes(ramDir);
		 * fsIndexWriter.close();
		 */

	}

	/**
	 * 读取源数据文件
	 *
	 * @param filePath
	 * @param indexWriter
	 * @throws Exception
	 */
	private void readFile(String filePath, IndexWriter indexWriter) throws Exception {
		File file = null;
		if (filePath != null && !filePath.trim().equals("")) {
			file = new File(filePath);
			if (!file.exists()) {
				throw new FileNotFoundException("not find file " + filePath);
			} else {
				if (file.isDirectory()) {
					File[] fs = file.listFiles();
					for (int i = 0; i < fs.length; i++) {
						if (fs[i].isDirectory()) {
							this.readFile(fs[i].getPath(), indexWriter);
						} else {
							this.writer(fs[i], indexWriter, names[i]);
						}
					}
				} else {
					this.writer(file, indexWriter, "");
				}
			}
		}
	}

	/**
	 * 将文件写入索引
	 *
	 * @param file
	 * @param indexWriter
	 * @throws Exception
	 */
	private void writer(File file, IndexWriter indexWriter, String name) throws Exception {
		Reader reader = new FileReader(file);
		Document document = new Document();
		document.add(new Field("content", reader, TextField.TYPE_NOT_STORED));
		document.add(new StringField("path", file.getPath(), Store.YES));
		// document.add(new StringField("path", file.getPath()+"123",
		// Store.YES));当出现2个相同的域时，域值会加在一起
		document.add(new LongField("size", file.length(), Store.YES));
		document.add(new LongField("lastTime", file.lastModified(), Store.YES));
		Field f = new TextField("name", name, Store.YES);
		f.setBoost(2);
		document.add(f);
		if (indexWriter.getConfig().getOpenMode() == IndexWriterConfig.OpenMode.CREATE) {
			indexWriter.addDocument(document);
		} else {
			indexWriter.updateDocument(new Term("path", file.getPath()), document);
		}
		// indexWriter.commit();
	}

	public void query(String str) throws Exception {
		this.dirReader = DirectoryReader.open(dir);
		IndexSearcher iSearcher = new IndexSearcher(dirReader);
		// TopScoreDocCollector topCollector = TopScoreDocCollector.create(100,
		// false);
		Term term = new Term("name", str);
		// ----
		Analyzer analyzer = new IKAnalyzer();
		QueryParser parser = new QueryParser(Version.LUCENE_42, "name", analyzer);
		Query query = parser.parse(str);
		// ----
		// Query query = new TermQuery(term);
		// WildcardQuery 通配符查询 FuzzyQuery 模糊查询
		// PrefixQuery 前缀查询
		// iSearcher.search(query, topCollector);
		this.result(iSearcher, query);
	}

	public void find(String str) throws Exception {
		this.dirReader = DirectoryReader.open(dir);
		MultiReader mReaders = new MultiReader(dirReader);
		IndexSearcher iSearcher = new IndexSearcher(mReaders);
		Analyzer analyzer = new IKAnalyzer();
		BooleanQuery query = new BooleanQuery();
		String[] field = { "name", "content" };
		BooleanClause.Occur[] flags = new BooleanClause.Occur[2];
		flags[0] = BooleanClause.Occur.SHOULD;
		flags[1] = BooleanClause.Occur.SHOULD;
		Query query1 = MultiFieldQueryParser.parse(Version.LUCENE_42, QueryParser.escape(str), field, flags,
				analyzer);
		query.add(query1, Occur.SHOULD);
		this.result(iSearcher, query1);
	}

	public void disMaxQuery(String str) throws Exception {
		this.dirReader = DirectoryReader.open(dir);
		IndexSearcher iSearcher = new IndexSearcher(dirReader);
		DisjunctionMaxQuery dquery=new DisjunctionMaxQuery(1);
		QueryParser parser = new QueryParser(Version.LUCENE_42, "name", analyzer);
		Query query = parser.parse(str);
		dquery.add(query);
		parser = new QueryParser(Version.LUCENE_42, "content", analyzer);
		query = parser.parse(str);
		dquery.add(query);
		Query query2=new RecencyBoostingQuery(dquery, 1000,10);
		this.result(iSearcher, query2);
	}

	private void result(IndexSearcher iSearcher, Query query) throws Exception {
		TopDocs topdocs = iSearcher.search(query,100);//NumericRangeFilter.newLongRange("size", (long) 1000, null, true, false) ,100, new Sort(new SortField("size", SortField.Type.LONG,true)));
		System.out.println("查询结果总数---" + topdocs.totalHits);
		System.out.println("查询结果最高分---" + topdocs.getMaxScore());

		// ScoreDoc[] hits = topCollector.topDocs(0, 100).scoreDocs;
		ScoreDoc[] hits = topdocs.scoreDocs;
		Formatter formatter = new SimpleHTMLFormatter("<span>", "</span>");
		Highlighter highlighter = new Highlighter(formatter, new QueryScorer(query));

		for (int i = 0; i < hits.length; i++) {
			List<IndexableField> list = iSearcher.doc(hits[i].doc).getFields();
			float s = hits[i].score;
			int shard = hits[i].shardIndex;
			for (IndexableField field : list) {
				String name = field.name();
				String value = field.stringValue();
				String frament = null;
				if ("content".equalsIgnoreCase(name)) {
					TokenStream ts = analyzer.tokenStream(name, new StringReader(value));
					frament = highlighter.getBestFragment(ts, value);
				} else if ("lastTime".equalsIgnoreCase(name)) {
					frament = DateTools.timeToString(Long.parseLong(value), Resolution.SECOND);

				} else {
					frament = value;
				}
				System.out.print(name + "  " + frament + "  " + s + "  ");
			}
			System.out.println();
		}
	}

	public String getFileContent(File file) throws Exception {
		Reader reader = new InputStreamReader(new FileInputStream(file), "GBK");
		BufferedReader br = new BufferedReader(reader);
		String result = "";
		while (br.readLine() != null) {
			result = result + "\n" + br.readLine();
		}
		br.close();
		reader.close();
		return result;
	}

	/**
	 * 删除索引
	 *
	 * @param indexWriter
	 * @throws IOException
	 */
	public void deleteIndex(IndexWriter indexWriter) throws IOException {
		Term t = new Term("path", new File("D:\\lucene\\data2\\LICENSE.txt").getPath());
		indexWriter.deleteDocuments(t);
		indexWriter.commit();
		// indexWriter.prepareCommit();2次提交，用于和数据库交互时的事物
		// indexWriter.rollback();
		// indexWriter.forceMergeDeletes();
		System.out.println("------del----");
	}

	public void ss(IndexReader dirReader) throws IOException {
		IndexReader newInedxReader = DirectoryReader.openIfChanged((DirectoryReader) dirReader, indexWriter,
				true);
		System.out.println(newInedxReader);
		System.out.println(newInedxReader == dirReader);
		/*
		 * if(newInedxReader!=null){ dirReader.close();
		 * dirReader=newInedxReader; }
		 */
	}

	public static void main(String[] args) throws Exception {
		Demo d = new Demo(true);
		// d.deleteIndex(d.indexWriter);
		// d.createIndex("D://lucene/data2");
		//d.find("那些年我们一起追过的女孩");
		d.disMaxQuery("那些年我们一起追过的女孩");
		// d.ss(d.dirReader);
		// d.indexWriter.forceMerge(1);
		// d.indexWriter.close();
	}

	/*
	 * 多条件联合查询 Term nterm = new Term("name", "*g??"); WildcardQuery
	 * wildcardQuery = new WildcardQuery(nterm);
	 * 
	 * Term aterm = new Term("address", "nanjing"); TermQuery termQuery = new
	 * TermQuery(aterm);
	 * 
	 * BooleanQuery query = new BooleanQuery(); query.add(wildcardQuery,
	 * BooleanClause.Occur.MUST); //should表示"或" must表示"必须" query.add(termQuery,
	 * BooleanClause.Occur.MUST);
	 * 
	 * TopDocs topDocs = searcher.search(query, 10);
	 */

}
